package cn.tworice.document.doc;

import cn.tworice.document.config.DocumentProperties;
import cn.tworice.document.vo.*;
import cn.tworice.common.util.CollectionUtil;
import cn.tworice.common.util.StringUtils;
import cn.tworice.upload.service.UploadUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHpsMeasure;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTInd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSpacing;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@Slf4j
public class DocxExecutor {

    @Resource
    private UploadUtil uploadUtil;

    @Resource
    private DocumentProperties documentProperties;

    private final Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");

    private final Map<String, Set<String>> variableMap = new HashMap<>();

    public void cacheClear() {
        variableMap.clear();
    }

    /**
     * 读取文档中的变量
     *
     * @param inputFilePath 文档路径，格式如：山西警察学院/开题报告.docx
     * @return 变量列表
     */
    public Set<String> loadVariable(String inputFilePath) {
        if (variableMap.containsKey(inputFilePath)) {
            return variableMap.get(inputFilePath);
        }
        Set<String> variables = new LinkedHashSet<>();
        try (FileInputStream fis = new FileInputStream(this.getDocumentPath(inputFilePath));
             XWPFDocument document = new XWPFDocument(fis)) {
            // 处理段落中的内容
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                this.loadText(paragraph, variables);
            }
            // 处理表格中的内容
            for (XWPFTable table : document.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        for (XWPFParagraph paragraph : cell.getParagraphs()) {
                            this.loadText(paragraph, variables);
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("文件读取错误");
        }
        variableMap.put(inputFilePath, variables);
        return variables;
    }

    /**
     * 处理文档内容并将处理后的内容输出到ByteArrayOutputStream
     *
     * @param outputStream ByteArrayOutputStream
     * @param vo   目标文档内容请求
     */
    public void processDocx(ByteArrayOutputStream outputStream, DownloadRequestVO vo) {
        try (FileInputStream fis = new FileInputStream(this.getDocumentPath(vo.getPath()));
             XWPFDocument document = new XWPFDocument(fis)) {
            // 构建替换表
            Map<String, String> replacements = this.buildReplacements(vo.getVariables());
            // 获取文档段落
            List<XWPFParagraph> paragraphList = document.getParagraphs();
            // 遍历段落，替换段落中的关键词
            for (int i=0; i < paragraphList.size(); i++) {
                XWPFParagraph paragraph = paragraphList.get(i);
                processText(document, paragraph, replacements);
            }

            // 获取文档中的表格，并遍历表格
            for (XWPFTable table : document.getTables()) {
                // 获取表格中的所有行
                for (XWPFTableRow row : table.getRows()) {
                    // 获取表格行中的每一列
                    for (XWPFTableCell cell : row.getTableCells()) {
                        // 获取表格单元格中的所有段落
                        List<XWPFParagraph> paragraphs = cell.getParagraphs();
                        // 遍历单元格中的段落
                        if(CollectionUtil.isNotEmpty(paragraphs)){
                            for (int i = 0; i < paragraphs.size(); i++) {
                                processTextToTable(cell, paragraphs.get(i), replacements);
                            }
                        }
                    }
                }
            }
            // 保存处理后的文档
            document.write(outputStream);

        } catch (IOException e) {
            log.error("文件读取错误", e);
        }
    }

    /**
     * 处理文本，提取并替换${xxx}格式的数据
     * @param document 当前文档
     * @param paragraph 当前段落
     */
    private void processText(XWPFDocument document, XWPFParagraph paragraph, Map<String, String> replacements) {
        Matcher matcher = pattern.matcher(paragraph.getText());
        while (matcher.find()) {
            // 命中的变量
            String variable = matcher.group(1);
            // 根据占位符名称找到替换的值
            String replacement = replacements.getOrDefault(variable, matcher.group(0));
            // 替换paragraph段落为其他内容
            replaceParagraphText(document, paragraph, this.getLines(replacement));
        }
    }

    /**
     * 将内容写入到文档表格中
     * @param tableCell 文档
     * @param paragraph 段落
     * @param replacements 替换表
     */
    private void processTextToTable(XWPFTableCell tableCell,XWPFParagraph paragraph, Map<String, String> replacements) {
        String text = paragraph.getText();
        Matcher matcher = pattern.matcher(text);
        while (matcher.find()) {
            String variable = matcher.group(1);
            // 根据占位符名称找到替换的值
            String replacement = replacements.getOrDefault(variable, matcher.group(0)); // 如果找不到替换值则保留原内容
            // 替换${xxx}为其他内容（例如 "replaced_value"）
            replaceCellParagraphText(tableCell, paragraph, this.getLines(replacement));
        }
    }

    /**
     * 对paragraph段落进行替换，每一个lines为一个新段落
     * @param document 文本
     * @param paragraph 命中关键词的行
     * @param lines 要替换的新文本
     */
    private void replaceParagraphText(XWPFDocument document, XWPFParagraph paragraph, String[] lines) {
        if (lines==null || lines.length == 0) {
            return;
        }

        /* runs长度不为零 */
        List<XWPFRun> runs = paragraph.getRuns();
        int beginRun = -1, endRun = -1, beginIndex = -1, endIndex = -1;
        // 查找 ${ 和 } 的位置
        for (int i = 0; i < runs.size(); i++) {
            XWPFRun run = runs.get(i);
            String text = run.text();
            if (beginRun == -1 && text.contains("$")) {
                beginRun = i;
                beginIndex = text.indexOf("$");
            }
            if (endRun == -1 && text.contains("}")) {
                endRun = i;
                endIndex = text.indexOf("}");
            }
            if (beginRun != -1 && endRun != -1) {
                break;  // 提高效率，一旦找到起始和结束符，退出循环
            }
        }

        // 判断 beginRun 和 endRun 是否找到并在同一个 run 中
        if (beginRun == -1 || endRun == -1) {
            return;  // 没有找到 ${ 或 }
        }

        if (beginRun == endRun) {
            // 同一 run 中替换
            replaceTextInSameRun(runs.get(beginRun), beginIndex, endIndex, lines[0]);
        } else {
            // 跨多个 run 替换
            replaceTextInMultipleRuns(runs, beginRun, endRun, beginIndex, endIndex, lines[0]);
        }

        // 追加后续的 line 为段落
        continuedWritingLine(lines, document, paragraph);
    }

    private void replaceTextInSameRun(XWPFRun run, int beginIndex, int endIndex, String replacement) {
        String text = run.text();
        String result = StringUtils.replaceSubstring(text, beginIndex, endIndex, replacement);
        run.setText(result, 0);  // 只替换指定的 text
    }

    private void replaceTextInMultipleRuns(List<XWPFRun> runs, int beginRun, int endRun, int beginIndex, int endIndex, String replacement) {
        // 处理起始和结束的 run
        setBeginAndEndRun(runs.get(beginRun), runs.get(endRun), beginIndex, endIndex);

        // 删除中间的多余 run
        for (int i = endRun - 1; i > beginRun; i--) {
            runs.get(i).setText("", 0);
        }

        // 在 endRun 前的 run 替换内容
        runs.get(endRun - 1).setText(replacement, 0);
    }

    /**
     * 续写后续的行，除了第一行
     * @param lines 行数据
     * @param document 当前文档
     * @param paragraph 目标段落
     */
    private void continuedWritingLine(String[] lines,XWPFDocument document,XWPFParagraph paragraph) {
        for (int i = 1; i < lines.length; i++) {
            // 使用目标段落的光标，并移动到段落的末尾
            XWPFParagraph newParagraph;
            int paragraphIndex = document.getParagraphs().indexOf(paragraph);
            if (document.getParagraphs().size() - 1 == paragraphIndex) { // 最后一个段落
                newParagraph = document.createParagraph();
            }else{ // 中间的段落
                newParagraph = document.insertNewParagraph(document.getParagraphs().get(paragraphIndex + 1).getCTP().newCursor());
            }
            // 创建运行
            XWPFRun newRun = newParagraph.createRun();
            newRun.setText(lines[i]);
            this.copyFormatting(newParagraph, paragraph);

            // 更新 paragraph 引用，以便后续的段落插入正确的位置
            paragraph = newParagraph;
        }
    }

    /**
     * 删除beginRun和endRun之间的run中的变量内容
     * @param beginRun 开始的run
     * @param endRun 结束的run
     * @param beginIndex 开始的run中的开始索引
     * @param endIndex 结束的
     */
    private void setBeginAndEndRun(XWPFRun beginRun, XWPFRun endRun, int beginIndex, int endIndex) {
        if (beginRun.text().length() == 1) {
            beginRun.setText("",0);
        }else{
            beginRun.setText(beginRun.text().substring(0, beginIndex),0);
        }

        if (endRun.text().length() == 1) {
            endRun.setText("",0);
        }else{
            endRun.setText(endRun.text().substring(endIndex),0);
        }

    }

    /**
     * 第一段正常，后面字体大小异常
     * 对表格单元格中的文本进行处理
     * @param cell 表格单元格
     * @param paragraph 命中关键词的行
     * @param lines 要替换的新文本
     */
    private void replaceCellParagraphText(XWPFTableCell cell, XWPFParagraph paragraph, String[] lines) {
        if (lines==null || lines.length == 0) {
            return;
        }
        // 将单元格中的所有运行删除
        for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) {
            paragraph.removeRun(i);
        }
        paragraph.createRun().setText(lines[0]);
        for (int i = 1; i < lines.length; i++) {
            if (StringUtils.isBlank(lines[i])) {
                continue;
            }

            // 创建新段落
            XWPFParagraph newParagraph = cell.addParagraph();
            // 创建运行
            XWPFRun newRun = newParagraph.createRun();
            newRun.setText(lines[i]);
            this.copyFormatting(newParagraph, paragraph);
        }
    }

    /**
     * 复制原段落样式
     * @param newParagraph 新段落
     * @param oldParagraph 旧段落
     */
    private void copyFormatting(XWPFParagraph newParagraph, XWPFParagraph oldParagraph) {
        // 获取旧段落的底层XML段落属性
        CTPPr oldPPr = oldParagraph.getCTP().getPPr();
        if (oldPPr == null) return;

        // 准备新段落的属性
        CTPPr newPPr = newParagraph.getCTP().getPPr();
        if (newPPr == null) {
            newPPr = newParagraph.getCTP().addNewPPr();
        }

        // 修复点：精确复制缩进属性（包括字符单位）
        if (oldPPr.isSetInd()) {
            CTInd oldInd = oldPPr.getInd();
            CTInd newInd = newPPr.isSetInd() ? newPPr.getInd() : newPPr.addNewInd();

            // 同时复制字符单位和缇单位（1字符=240缇）
            if (oldInd.isSetFirstLineChars()) {
                newInd.setFirstLineChars(oldInd.getFirstLineChars());
            }
            if (oldInd.isSetFirstLine()) {
                newInd.setFirstLine(oldInd.getFirstLine());
            }

            // 保留其他缩进属性
            if (oldInd.isSetLeftChars()) newInd.setLeftChars(oldInd.getLeftChars());
            if (oldInd.isSetLeft()) newInd.setLeft(oldInd.getLeft());
            if (oldInd.isSetRightChars()) newInd.setRightChars(oldInd.getRightChars());
            if (oldInd.isSetRight()) newInd.setRight(oldInd.getRight());
            if (oldInd.isSetHangingChars()) newInd.setHangingChars(oldInd.getHangingChars());
            if (oldInd.isSetHanging()) newInd.setHanging(oldInd.getHanging());
        }

        // 3. 复制间距属性（支持倍距和固定值）
        if (oldPPr.isSetSpacing()) {
            CTSpacing oldSpacing = oldPPr.getSpacing();
            CTSpacing newSpacing = newPPr.isSetSpacing() ? newPPr.getSpacing() : newPPr.addNewSpacing();

            // 复制行距规则和值（关键修复点）
            if (oldSpacing.isSetLine()) {
                newSpacing.setLine(oldSpacing.getLine());
                newSpacing.setLineRule(oldSpacing.getLineRule());
            }

            // 复制段前段后间距
            if (oldSpacing.isSetBefore()) newSpacing.setBefore(oldSpacing.getBefore());
            if (oldSpacing.isSetAfter()) newSpacing.setAfter(oldSpacing.getAfter());
        }

        // 4. 复制字体属性（保留原有逻辑）
        if (!oldParagraph.getRuns().isEmpty() && !newParagraph.getRuns().isEmpty()) {
            XWPFRun oldRun = oldParagraph.getRuns().get(0);
            XWPFRun newRun = newParagraph.getRuns().get(0);

            // 字体大小复制（含样式回退）
            int fontSize = oldRun.getFontSize();
            if (fontSize == -1 && oldParagraph.getStyle() != null) {
                XWPFStyle style = oldParagraph.getDocument().getStyles().getStyle(oldParagraph.getStyle());
                if (style != null && style.getCTStyle().isSetRPr()) {
                    CTHpsMeasure sz = style.getCTStyle().getRPr().getSz();

                    if (sz != null) fontSize = new Integer(sz.getVal().toString()) / 2;
                }
            }
            if (fontSize != -1) newRun.setFontSize(fontSize);

            // 复制其他字体属性（字体、颜色等）
            newRun.setFontFamily(oldRun.getFontFamily());
            newRun.setColor(oldRun.getColor());
        }
    }


    /**
     * 读取当前行中的变量
     *
     * @param paragraph 行数据
     * @param variables 变量集合
     */
    private void loadText(XWPFParagraph paragraph, Set<String> variables) {
        String text = paragraph.getText();
        Matcher matcher = pattern.matcher(text);
        while (matcher.find()) {
            String variable = matcher.group(1);
            variables.add(variable);
        }
    }

    private String getDocumentPath(String filePath) {
        return uploadUtil.getUploadPath() + documentProperties.getDocument() + File.separator + filePath;
    }

    /**
     * 构建替换内容的Map
     * @param variables 变量列表
     * @return 变量表
     */
    private Map<String, String> buildReplacements(List<GenerateResVO> variables) {
        Map<String, String> replacements = new HashMap<>();
        variables.forEach(item -> replacements.put(item.getVariable(), item.getValue()));
        return replacements;
    }

    /**
     * 获取每一行文字
     * @param replacement 准备替换什么内容
     * @return 返回以换行符分割的数组
     */
    private String[] getLines(String replacement) {
        if (StringUtils.isEmpty(replacement)) {
            return null;
        }
        return replacement.replace("\n\n", "\n")
                .split("\n");
    }
}
